package com.ddwl.user.service;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.ddwl.common.constant.*;
import com.ddwl.common.dao.model.BaseModel;
import com.ddwl.common.exception.CommonException;
import com.ddwl.common.exception.ExceptionCode;
import com.ddwl.common.exception.ParamException;
import com.ddwl.common.feign.api.*;
import com.ddwl.common.feign.thirdparty.DingTalkApi;
import com.ddwl.common.service.AuthService;
import com.ddwl.common.service.RedisService;
import com.ddwl.common.util.*;
import com.ddwl.schema.bo.base.PageBo;
import com.ddwl.schema.bo.data.OperateLogVo;
import com.ddwl.schema.bo.gate.DingTalkBo;
import com.ddwl.schema.bo.gate.RegionBo;
import com.ddwl.schema.bo.message.TempSentSmsBo;
import com.ddwl.schema.bo.user.*;
import com.ddwl.schema.bo.wallet.IssueCouponBo;
import com.ddwl.schema.bo.wallet.ProvideSubsidyBo;
import com.ddwl.schema.vo.base.PageVo;
import com.ddwl.schema.vo.base.RestVo;
import com.ddwl.schema.vo.dist.UserInviteCountVo;
import com.ddwl.schema.vo.gate.DingTalkRestVo;
import com.ddwl.schema.vo.gate.RegionVo;
import com.ddwl.schema.vo.order.StatisticsUserVo;
import com.ddwl.schema.vo.shop.ShopVo;
import com.ddwl.schema.vo.user.*;
import com.ddwl.user.constant.RegisterStatusEnum;
import com.ddwl.user.dao.*;
import com.ddwl.user.dao.dto.UserInviteCountQueryDto;
import com.ddwl.user.dao.model.*;
import com.ddwl.user.model.ShareRegisterRelation;
import com.ddwl.user.util.InvitationCodeUtil;
import com.ddwl.user.util.WXBizMsgCrypt;
import com.ddwl.user.util.WechatUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import io.gitee.tooleek.lock.spring.boot.annotation.Lock;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author Fu Zhaohui
 * @version 1.0.0
 * @date 2019/6/14 7:35
 */
@RefreshScope
@Slf4j
@Service
@RequiredArgsConstructor
public class UserService {

    private final UserDao userDao;
    private final IdentityCardServer identityCardServer;
    private final AuthService authService;
    private final UserDistService userDistService;
    private final TokenService tokenService;
    private final RedisService redisService;
    private final DistrConfigServer distrConfigServer;
    private final UserAuthDao userAuthDao;
    private final IdentityCardDao identityCardDao;
    private final UserStatusDao userStatusDao;
    private final StringRedisTemplate template;
    @Value("${ddwl.sms.code_max}")
    private Integer codeMax;
    private final GateApi gateApi;
    private final MessageApi messageApi;
    private final DataApi dataApi;
    private final WalletApi walletApi;
    private final ShopApi shopApi;
    private final UserLevelService userLevelService;
    private final ShareRegisterRelationDao shareRegisterRelationDao;
    private final DingTalkApi dingTalkApi;

    @Value("${inviteSubsidy}")
    private BigDecimal inviteSubsidy;
    @Value("${dingtalk.exception_robot.access_token}")
    private String accessToken;


    /**
     * 用户注册接口
     *
     * @param bo
     * @return
     */
    @Lock(leaseTime = 5)
    @Transactional(rollbackFor = Exception.class)
    public UserVo reg(RegisterBo bo) {
        String key = verifySMSCaptcha(bo.getMobile(), bo.getSmsCaptcha(), SmsCaptchaNodeEnum.REGISTER);

        User entity = userDao.queryByMobile(bo.getMobile());
        if (null == entity) {
            entity = createUser(bo);
        } else {
            if (BaseEnum.ProductEnum.FENXIAO.getName().equals(bo.getProduct())) {
                ParamException.isTrue(Boolean.TRUE, RegisterStatusEnum.MOBILE_REGISTERED.getDesc());
            }
            User update = new User();
            update.setId(entity.getId());
            if (BaseEnum.ProductEnum.BYTE_DANCE.getName().equals(bo.getProduct())) {
                if (!StringUtils.hasText(entity.getByteUnionId())) {
                    update.setByteUnionId(bo.getUnionId());
                    userDao.updateByPrimaryKeySelective(update);
                }
            } else {
                if (!StringUtils.hasText(entity.getUnionId())) {
                    update.setUnionId(bo.getUnionId());
                    userDao.updateByPrimaryKeySelective(update);
                }
            }
        }
        UserAuth auth = userAuthDao.getByUserId(entity.getId(), bo.getProduct());
        if (null == auth) {
            createUserAuth(bo, entity.getId());
        } else {
            ParamException.isTrue(!auth.getOpenId().equals(bo.getWxOpenId()), "该手机号已经绑定其它账号");
        }
        UserVo vo = login(bo, entity);
        addShareRegisterRelation(bo.getShareUserId(), entity.getId());

        redisService.getHashOps().increment(RedisKey.DATA_TODAY_CHART.getKey(), DataEnum.TodayData.REGISTERS.name(), 1);
        template.delete(key);
        return vo;
    }

    private void addShareRegisterRelation(String shareUserId, String registerUserId) {
        if (StrUtil.isNotEmpty(shareUserId)) {
            ShareRegisterRelation srr = new ShareRegisterRelation();
            srr.setShareUserId(shareUserId);
            srr.setRegisterUserId(registerUserId);
            shareRegisterRelationDao.insert(srr);
        }
    }

    private UserVo login(RegisterBo bo, User entity) {
        LoginBo loginBo = BeanConverUtils.convertBean(bo, LoginBo.class);
        loginBo.setId(entity.getId());
        loginBo.setNickname(entity.getNickname());
        UserVo vo = BeanConverUtils.convertBean(entity, UserVo.class);
        vo.setToken(tokenService.getToken(loginBo));
        return vo;
    }

    @Lock(leaseTime = 5)
    @Transactional(rollbackFor = Exception.class)
    public UserVo wxMobileReg(WxMobileRegisterBo wxMobileRegisterBo) {
        ParamException.isNull(wxMobileRegisterBo.getPhoneEncryptedData(), "微信绑定信息为空");
        ParamException.isNull(wxMobileRegisterBo.getPhoneIv(), "解密密钥为空");
        ParamException.isNull(wxMobileRegisterBo.getSessionKey(), "会话密钥为空");
        User wbm = userDao.getByUnionId(Optional.ofNullable(wxMobileRegisterBo.getUnionId()).orElse(redisService.getHashOps().get(RedisKey.WX_SESSION_KEY.getKey(wxMobileRegisterBo.getSessionKey()), "unionId")));
        UserVo vo = new UserVo();
        JSONObject wxPhoneNumberInfo = WechatUtil.decryptData(wxMobileRegisterBo.getPhoneEncryptedData(), wxMobileRegisterBo.getSessionKey(), wxMobileRegisterBo.getPhoneIv());
        log.info("微信解密得到数据：{}", wxPhoneNumberInfo);
        String mobile = wxPhoneNumberInfo.getString("phoneNumber");
        if (StrUtil.isEmpty(mobile)) {
            log.error(RegisterStatusEnum.DECRYPTED_DATA_ERROR.getDesc());
            vo.setRegisterStatus(RegisterStatusEnum.DECRYPTED_DATA_ERROR.getCode());
            return vo;
        }
        RegisterBo bo = BeanConverUtils.convertBean(wxMobileRegisterBo, RegisterBo.class);
        User user = userDao.queryByMobile(mobile);
        // 如果手机号用户已经存在了unionId与openId合并到手机号用户上，并更新tk绑定的用户使用手机号的userId，同时标计当前用户被合并后期清除
        if (null != user) {
            // 如果手机号用户绑定了unionId
            if (StringUtils.hasText(user.getUnionId()) && !user.getUnionId().equals(wbm.getUnionId())) {
                vo.setRegisterStatus(RegisterStatusEnum.MOBILE_REGISTERED.getCode());
                return vo;
            }
            user.setUnionId(wbm.getUnionId());
            user.setWxaCodeUnlimit(getWxaCodeUnlimitUrl(user.getId()));
            this.addInviteInfo(bo, user);
            userDao.updateByPrimaryKeySelective(user);
            userAuthDao.mergeAuth(wbm.getId(), user.getId());
            userDao.deleteById(wbm.getId(), user.getId());
            vo = login(bo, user);

            // 手机号未被注册 将手机号更新到当前用户
        } else {
            wbm.setMobile(mobile);
            wbm.setWxaCodeUnlimit(getWxaCodeUnlimitUrl(wbm.getId()));
            this.addInviteInfo(bo, wbm);
            userDao.updateByPrimaryKeySelective(wbm);
            vo = login(bo, wbm);
            addShareRegisterRelation(bo.getShareUserId(), wbm.getId());
            //异步更新
//            ((UserService) AopContext.currentProxy()).asyncUpdate(wxMobileRegisterBo.getProduct(), wxMobileRegisterBo.getWxOpenId(), fenXiao, flag, entity.getId());
        }
        return vo;
    }

    private User createUser(RegisterBo bo) {
        User entity = BeanConverUtils.convertBean(bo, User.class);
        entity.setId(UUIDGenerator.getShortUUID());
        entity.setProduct(bo.getProduct());
        //默认用户0级
        QueryUserLevelBo bo1 = new QueryUserLevelBo();
        UserLevelVo userLevelVo = userLevelService.getList(bo1).get(0);
        entity.setUserLevelId(userLevelVo.getId());

        if (StrUtil.isNotBlank(bo.getPassword())) {
            entity.setSalt(RandomGenerateUtil.getRandomS(0, 6));
            entity.setPassword(EncryptUtil.encryptMD5(entity.getSalt() + bo.getPassword()));
        }
        entity.setType(UserEnum.UserTypeEnum.MEMBER.getValue());
        entity.setRegionId(bo.getRegionId());
        if (StrUtil.isBlank(bo.getNickname())) {
            entity.setNickname(CommonUtils.mobileEncrypt(bo.getMobile()));
        }
        //获取邀请码自增序列
        Long index = redisService.getValueOps().increment(RedisKey.DATA_INVITATION_INCR.getKey(), 1);
        entity.setInviteCode(InvitationCodeUtil.idToCode(index));
        addInviteInfo(bo, entity);
        userDao.insert(entity);
        return entity;
    }

    private void addInviteInfo(RegisterBo bo, User entity) {
        if (!StringUtils.hasText(bo.getInviteId()) && !StringUtils.hasText(bo.getShareUserId()) && !StringUtils.hasText(bo.getInviteCode())) {
            return;
        }
        User sup;
        if (StringUtils.hasText(bo.getInviteCode())) {
            String regionId = redisService.getValueOps().get(RedisKey.INVITE_REGION_KEY.getKey(bo.getInviteCode()));
            if (StringUtils.hasText(regionId)) {
                entity.setRegionId(regionId);
                return;
            } else {
                sup = userDao.queryByInviteCode(bo.getInviteCode().toUpperCase());
                ParamException.isNull(sup, "邀请码不正确");
            }
        } else {
            sup = userDao.selectByPrimaryKey(Optional.ofNullable(bo.getShareUserId()).orElse(bo.getInviteId()));
        }
        ParamException.isNull(sup, "邀请人不存在");
//        if (null == sup) {
//            sup = userDao.queryCompanyAccount();
//            ParamException.isNull(sup, "公司账号未生成，请稍后注册");
//        }
        fillUserSuperiorInfo(entity, sup);
        addPopularizeNum(entity);
        inviteSubsidy(entity);
    }

    private void createUserAuth(RegisterBo bo, String userId) {
        if (StrUtil.isNotBlank(bo.getWxOpenId())) {
            UserAuth userAuth = userAuthDao.getByOpenId(bo.getWxOpenId());
            if (userAuth == null) {
                userAuth = BeanConverUtils.convertBean(bo, UserAuth.class);
                userAuth.setAppId(bo.getAppId());
                userAuth.setUserId(userId);
                userAuth.setOpenId(bo.getWxOpenId());
                userAuth.setProduct(bo.getProduct());
                userAuthDao.add(userAuth);
            }
        }
    }

    private String decryptUnionId(String encryptedData, String sessionKey, String iv) {
        if (StrUtil.isNotEmpty(encryptedData) && sessionKey.length() > 64 && StrUtil.isNotEmpty(iv)) {
            JSONObject wxUserInfo = WechatUtil.decryptData(encryptedData, sessionKey, iv);
            log.info("微信解密得到数据：{}", wxUserInfo);
            String unionId = wxUserInfo.getString("unionId");
            if (!"undefined".equals(unionId)) {
                return unionId;
            }
        }
        return null;
    }

    /**
     * 商城创建分销员接口
     *
     * @param bo
     * @return
     */
    @Lock(leaseTime = 5)
    @Transactional(rollbackFor = Exception.class)
    public void fenxiaoRegister(FenxiaoRegisterBo bo) {
        //生成身份证认证信息
        IdentitySubmitBo identitySubmitBo = new IdentitySubmitBo();
        identitySubmitBo.setUserId(bo.getId());
        identitySubmitBo.setIdName(bo.getIdName());
        identitySubmitBo.setIdCard(bo.getIdCard());
        identitySubmitBo.setRegionId(bo.getRegionId());
        identitySubmitBo.setAdder(bo.getAdder());
        identitySubmitBo.setIdCardFront(bo.getIdCardFront());
        identitySubmitBo.setIdCardBack(bo.getIdCardBack());
        identityCardServer.submitfenxiaoProfit(identitySubmitBo);
    }

    private void inviteSubsidy(User entity) {
        ProvideSubsidyBo provideSubsidyBo = new ProvideSubsidyBo();
        provideSubsidyBo.setUserId(entity.getImmediateSuperior());
        provideSubsidyBo.setFlowId(entity.getId());
        provideSubsidyBo.setMsg("邀请新人送津贴");
        provideSubsidyBo.setType(1);
        provideSubsidyBo.setRewardSubsidy(inviteSubsidy);
        walletApi.provideSubsidy(provideSubsidyBo);
    }

    private void fillUserSuperiorInfo(User entity, User sup) {
        entity.setIndirectSuperior(sup.getImmediateSuperior());
        entity.setIndirectSuperiorName(sup.getImmediateSuperiorName());
        entity.setImmediateSuperior(sup.getId());
        entity.setImmediateSuperiorName(sup.getNickname());
        entity.setType(UserEnum.UserTypeEnum.DIST.getValue());
        entity.setTopAgent(sup.getTopAgent());
        if (StrUtil.isEmpty(entity.getRegionId())) {
            entity.setRegionId(sup.getRegionId());
        }
    }

    private void updateUserWxaCode(String userId) {
        User entity = new User();
        entity.setId(userId);
        entity.setWxaCodeUnlimit(getWxaCodeUnlimitUrl(userId));
        userDao.updateByPrimaryKeySelective(entity);
    }

    public String getWxaCodeUnlimitUrl(String id) {
        RestVo<String> stringRestVo = gateApi.wxaCodeUnlimit(id, BaseEnum.WxAppId.WBM.getCode(), String.format(GateEnum.WxaPath.USER_WXA_CODE.getPath(), id), null);
        if (StrUtil.isEmpty(stringRestVo.getResult())) {
            return null;
        }
        return stringRestVo.getResult();
    }

    /**
     * 用户推广数+1 popularize
     *
     * @param user
     */
    public void addPopularizeNum(User user) {
        if (StrUtil.isNotEmpty(user.getImmediateSuperior())) {
            UserDistStatistical uds = userDistService.getUserDistStatistical(user.getImmediateSuperior());
            UserDistStatistical entity = new UserDistStatistical();
            entity.setId(uds.getId());
            if (BaseEnum.ProductEnum.MALL.getName().equals(user.getProduct())) {
                entity.setTotalMallUseUser(Optional.ofNullable(uds.getTotalMallUseUser()).orElse(0) + 1);
            } else {
                entity.setTotalUseUser(Optional.ofNullable(uds.getTotalUseUser()).orElse(0) + 1);
            }
            userDistService.updatePopularizeNum(entity);
            //更新用户等级
            updateUserLevel(user.getImmediateSuperior());
        }
    }

    /**
     * 更新用户等级
     */
    public void updateUserLevel(String userId) {
        ParamException.isTrue(StrUtil.isBlank(userId), "用户Id不能为空");
        UserInviteCountQueryDto bo = new UserInviteCountQueryDto();
        bo.setUserId(userId);
        UserInviteCountVo userInviteCountVo = userDistService.queryUserInviteCountNew(bo);
        QueryUserLevelBo bo1 = new QueryUserLevelBo();
        List<UserLevelVo> levelVos = userLevelService.getList(bo1).stream()
                .sorted(Comparator.comparing(UserLevelVo::getUserLevelCode).reversed()).collect(Collectors.toList());
        for (UserLevelVo o : levelVos) {
            if ((userInviteCountVo.getDistTotal() == null ? 0 : userInviteCountVo.getDistTotal()) >= o.getLevelUpPeople()) {
                User user = new User();
                user.setId(userId);
                user.setUserLevelId(o.getId());
                userDao.updateByPrimaryKeySelective(user);
                break;
            }
        }
    }


    /**
     * 用户推广数 -1 popularize
     *
     * @param user
     */
    private void subtractPopularizeNum(User user) {
        if (StrUtil.isNotEmpty(user.getImmediateSuperior())) {
            UserDistStatistical uds = userDistService.getUserDistStatistical(user.getImmediateSuperior());
            UserDistStatistical entity = new UserDistStatistical();
            entity.setId(uds.getId());
            if (BaseEnum.ProductEnum.MALL.getName().equals(user.getProduct())) {
                Integer totalMallUser = null == uds.getTotalMallUseUser() ? 0 : uds.getTotalMallUseUser();
                if (totalMallUser.compareTo(0) > 0) {
                    entity.setTotalMallUseUser(totalMallUser - 1);
                    userDistService.updatePopularizeNum(entity);
                }
            } else {
                Integer totalUseUser = null == uds.getTotalUseUser() ? 0 : uds.getTotalUseUser();
                if (totalUseUser.compareTo(0) > 0) {
                    entity.setTotalUseUser(totalUseUser - 1);
                    userDistService.updatePopularizeNum(entity);
                }
            }
        }
    }


    public Boolean check(String mobile) {
        User entity = userDao.queryByMobile(mobile);
        return entity != null;
    }

    public CheckMobileVo checkMobile(String mobile) {
        User entity = userDao.queryByMobile(mobile);
        CheckMobileVo checkMobileVo = new CheckMobileVo();
        checkMobileVo.setIsReg(entity != null);
        if (entity != null) {
            checkMobileVo.setIsReg(true);
            checkMobileVo.setIsPassword(StrUtil.isNotBlank(entity.getPassword()));
        } else {
            checkMobileVo.setIsReg(false);
            checkMobileVo.setIsPassword(false);
        }
        return checkMobileVo;
    }

    public UserVo getById(String userId) {
        User entity = userDao.selectByPrimaryKey(userId);
        UserVo vo = BeanConverUtils.convertBean(entity, UserVo.class);
        if (vo != null) {
            vo.setProfitRatio(distrConfigServer.getLevelRate(entity.getProfitLevel()));
        }
        return vo;
    }

    public UserVo getSimpleUserById(String userId) {
        User entity = userDao.selectByPrimaryKey(userId);
        return BeanConverUtils.convertBean(entity, UserVo.class);
    }

    public UserVo getByPhone(String phone) {
        User user = new User();
        user.setMobile(phone);
        User entity = userDao.selectOne(user);
        return BeanConverUtils.convertBean(entity, UserVo.class);
    }

    public UserVo getByUserId(String id) {
        User user = userDao.selectByPrimaryKey(id);
        return BeanConverUtils.convertBean(user, UserVo.class);
    }

    public void updateUserAgent(String userId, List<String> regions) {
        User user = new User();
        user.setId(userId);
        user.setAgentRegion(StrUtil.join(Constant.DEFAULT_SEPARATOR, regions));
        userDao.updateByPrimaryKeySelective(user);
    }

    public List<UserVo> getByIds(Collection<String> userIds) {
        List<User> entity = userDao.selectByPrimaryKeys(userIds);
        List<UserVo> userVos = BeanConverUtils.convertBeanList(entity, UserVo.class);
        return userVos;
    }


    @Transactional(rollbackFor = Throwable.class)
    public void update(EditUserInfoBo bo) {
        User entity = userDao.selectByPrimaryKey(bo.getId());
        if (entity == null) {
            throw new CommonException(ExceptionCode.DATA_NOT_EXISTS);
        }
        if (StrUtil.isNotEmpty(bo.getNickname()) && !bo.getNickname().equals(entity.getNickname())) {
            userDao.updateSuperiorNickname(bo.getId(), bo.getNickname());
        }
        entity = BeanConverUtils.convertBean(bo, User.class);
        userDao.updateByPrimaryKeySelective(entity);
    }

    @Transactional(rollbackFor = Throwable.class)
    public void bindInviteCode(String userId, String inviteCode) {
        User user = userDao.selectByPrimaryKey(userId);
        ParamException.isNull(user, "用户信息不正确");
        ParamException.isNull(UserEnum.UserTypeEnum.MEMBER.getValue().equals(user.getType()), "不能修改邀请码");
        User sup = userDao.queryByInviteCode(inviteCode);
        ParamException.isNull(sup, "请输入正确的邀请码");
        User update = new User();
        update.setId(user.getId());
        update.setIndirectSuperior(sup.getImmediateSuperior());
        update.setIndirectSuperiorName(sup.getImmediateSuperiorName());
        update.setImmediateSuperior(sup.getId());
        update.setImmediateSuperiorName(sup.getNickname());
        update.setType(UserEnum.UserTypeEnum.DIST.getValue());
        userDao.updateByPrimaryKeySelective(update);
    }


    @Transactional(rollbackFor = Throwable.class)
    public void bindMobile(AuthBo bo) {
        User entity = userDao.selectByPrimaryKey(bo.getUserId());
        if (entity == null) {
            throw new CommonException(ExceptionCode.DATA_NOT_EXISTS);
        }
        if (StringUtils.hasText(entity.getMobile())) {
            throw new CommonException("您已绑定过手机号码");
        }
        if (StringUtils.hasText(bo.getEncryptedData())) {
            String data = WXBizMsgCrypt.decryptData(bo.getEncryptedData(), bo.getSessionKey(), bo.getIv());
            log.info("绑定手机号码{}", data);
            entity.setMobile(JSON.parseObject(data).getString("phoneNumber"));
        } else {
            String key = verifySMSCaptcha(bo.getMobile(), bo.getSmsCaptcha(), SmsCaptchaNodeEnum.BIND_MOBILE);
            template.delete(key);
            entity.setMobile(bo.getMobile());
        }
        User mobileUser = userDao.queryByMobile(entity.getMobile());
        if (mobileUser == null) {
            userDao.updateByPrimaryKeySelective(entity);
            return;
        }
        if (StringUtils.hasText(mobileUser.getUnionId())) {
            throw new CommonException("手机号码已被绑定");
        }
        userDao.updateByPrimaryKeySelective(mobileUser);
        userAuthDao.mergeAuth(entity.getId(), mobileUser.getId());
        userDao.deleteById(entity.getId(), mobileUser.getId());
        authService.mergeAuth(bo.getProduct(), entity.getId(), mobileUser.getId());
    }

    /**
     * 绑定微信开放用户
     *
     * @param bo
     */
    @Transactional(rollbackFor = Throwable.class)
    public void bindWxOpenUser(WxUserBo bo) {
        User entity = userDao.selectByPrimaryKey(bo.getUserId());
        if (entity == null) {
            throw new CommonException(ExceptionCode.DATA_NOT_EXISTS);
        }
        entity.setNickname(bo.getNickName());
        entity.setSex(bo.getSex());
        entity.setAvatar(bo.getAvatar());
        if (!StringUtils.hasText(entity.getUnionId())) {
            entity.setUnionId(bo.getUnionId());
        }
        if (!StringUtils.hasText(entity.getRegionId())) {
            RegionBo regionBo = new RegionBo();
            regionBo.setProvinceName(bo.getProvince());
            regionBo.setCityName(bo.getCity());
            entity.setRegionId(gateApi.getCityId(regionBo).getResult());
        }
        userDao.updateByPrimaryKeySelective(entity);
        UserAuth userAuth = userAuthDao.getByOpenId(bo.getWxOpenId());
        if (userAuth == null) {
            userAuth = BeanConverUtils.convertBean(bo, UserAuth.class);
            userAuth.setOpenId(bo.getWxOpenId());
            userAuthDao.add(userAuth);
        }
    }

    @Transactional(rollbackFor = Throwable.class)
    public void unbindWxOpenUser(WxUserBo bo) {
        userAuthDao.deleteByUserId(bo.getUserId(), bo.getProduct());
    }

    @Transactional(rollbackFor = Throwable.class)
    public void changePwd(String userId, String oldPwd, String newPwd) {
        User entity = userDao.selectByPrimaryKey(userId);
        if (entity == null) {
            throw new CommonException(ExceptionCode.DATA_NOT_EXISTS);
        }
        String saltPassword = EncryptUtil.encryptMD5(entity.getSalt() + oldPwd);
        if (!saltPassword.equals(entity.getPassword())) {
            throw new CommonException("旧密码错误");
        }
        entity.setPassword(EncryptUtil.encryptMD5(entity.getSalt() + newPwd));
        userDao.updateByPrimaryKeySelective(entity);
    }

    @Transactional(rollbackFor = Throwable.class)
    public void resetPwd(RegBo bo) {
        authService.validMobileCode(bo.getMobile(), bo.getCode());
        User entity = userDao.queryByMobile(bo.getMobile());
        if (entity == null) {
            throw new CommonException(ExceptionCode.DATA_NOT_EXISTS);
        }
        entity.setPassword(EncryptUtil.encryptMD5(entity.getSalt() + bo.getPassword()));
        userDao.updateByPrimaryKeySelective(entity);
        authService.clearUserToken(entity.getId());
    }


    public PageVo<UserVo> page(PageBo<UserSearchBo> pageBo) {
        PageHelper.startPage(pageBo.getPage(), pageBo.getPageSize(), MySqlUtil.orderBy(pageBo.getSort(), pageBo.isAsc()));
        PageInfo<User> info = new PageInfo(userDao.queryPage(pageBo.getParam()));
        List<UserVo> list = BeanConverUtils.convertBeanList(info.getList(), UserVo.class);
        return new PageVo<>(info.getTotal(), pageBo.getPage(), pageBo.getPageSize(), list);
    }

    public List<StatisticsUserVo> pageUserCount(UserSearchBo pageBo) {
        List<StatisticsUserVo> statisticsUserVos = userDao.queryUserCount(pageBo);
        return statisticsUserVos;
    }


    /**
     * 更新用户的代理等级
     *
     * @param userId
     * @param level
     */
    public void updateProfitLevel(String userId, Integer level) {
        User entity = userDao.selectByPrimaryKey(userId);
        ParamException.isNull(entity);
        User user = new User();
        user.setId(entity.getId());
        user.setProfitLevel(level);
        userDao.updateByPrimaryKeySelective(user);
    }

    /**
     * 移除用户的分销信息
     *
     * @param userId
     */
    public void removeUserDist(String userId) {
        userDao.removeUserDist(userId);
    }

    /**
     * 获取会员微信绑定信息
     *
     * @param userId
     */
    public UserAuthVo getUserAuth(String userId, String product) {
        UserAuth userAuth = userAuthDao.getByUserId(userId, product);
        return BeanConverUtils.convertBean(userAuth, UserAuthVo.class);
    }

    @Transactional(rollbackFor = Throwable.class)
    public UserAuthVo getUserAuthByOpenId(RegisterBo bo) {
//        if (!redisService.getValueOps().setIfAbsent(RedisKey.LOCKED_KEY.getKey(bo.getUnionId()), String.valueOf(System.currentTimeMillis()), 2, TimeUnit.SECONDS)) {
//            throw new GlobalException("数据正在处理中，请稍后...");
//        }
        User user = userDao.getByUnionId(bo.getUnionId());
        if (user == null) {
            user = this.createUser(bo);
        }
        UserAuth userAuth = userAuthDao.getByOpenId(bo.getWxOpenId());
        if (userAuth == null) {
            userAuth = new UserAuth();
            userAuth.setUserId(user.getId());
            userAuth.setOpenId(bo.getWxOpenId());
            userAuth.setProduct(bo.getProduct());
            userAuth.setAppId(bo.getAppId());
            userAuthDao.add(userAuth);
        }
        UserAuthVo vo = BeanConverUtils.convertBean(userAuth, UserAuthVo.class);
        return vo;
    }

    private String getRedisKey(String mobile, SmsCaptchaNodeEnum node) {
        String key = null;
        switch (node) {
            case REGISTER:
                key = RedisKey.MOBILE_REG_SMS_KEY.getKey(mobile);
                break;
            case LOGIN:
                key = RedisKey.MOBILE_LOGIN_SMS_KEY.getKey(mobile);
                break;
            case RESET_PASSWORD:
                key = RedisKey.MOBILE_REST_SMS_KEY.getKey(mobile);
                break;
            case BIND_MOBILE:
                key = RedisKey.MOBILE_BIND_SMS_KEY.getKey(mobile);
                break;
        }
        return key;
    }


    public String getRestKey(String mobile, String code) {
        User user = userDao.queryByMobile(mobile);
        ParamException.isNull(user, "该手机号还未注册");
        String key = verifySMSCaptcha(mobile, code, SmsCaptchaNodeEnum.RESET_PASSWORD);
        String token = IdUtil.simpleUUID();
        redisService.getValueOps().set(RedisKey.USER_PASSWORD_RESET_TOKEN_KEY.getKey(user.getId()), token, 5, TimeUnit.MINUTES);
        template.delete(key);
        return token;
    }

    /**
     * 发送短信验证码
     */
    public SentSmsVo sendSMSCaptcha(SentSmsBo bo) {
        SentSmsVo sentSmsVo = new SentSmsVo();
        String maxKey = RedisKey.MOBILE_SMS_DAY_COUNT_KEY.getKey(bo.getMobile());
        String smsMax = redisService.getValueOps().get(maxKey);
        if (StrUtil.isNotBlank(smsMax)) {
            ParamException.isTrue(Integer.valueOf(smsMax) > codeMax, "今日收取短信上线以达到");
        }
        User user = userDao.queryByMobile(bo.getMobile());
        switch (SmsCaptchaNodeEnum.getByValue(bo.getType())) {
//            case REGISTER:
//                ParamException.isNotNull(user, "该手机号已注册");
//                break;
            case BIND_MOBILE:
                ParamException.isNotNull(user, "该手机号已注册");
                break;
            case LOGIN:
                ParamException.isNull(user, "该手机号未注册");
                break;
            case RESET_PASSWORD:
                ParamException.isNull(user, "该手机号未注册");
                break;
        }
        String key = getRedisKey(bo.getMobile(), SmsCaptchaNodeEnum.getByValue(bo.getType()));
        String code = redisService.getValueOps().get(key);
        if (StrUtil.isEmpty(code)) {
            code = RandomUtil.randomString(RandomUtil.BASE_NUMBER, 4);
        }
        TempSentSmsBo sentSmsBo = getTemplateParam(bo.getType(), code);
        sentSms(sentSmsBo, bo.getMobile());
        redisService.getValueOps().set(key, code, 5, TimeUnit.MINUTES);
        if (StrUtil.isBlank(smsMax)) {
            Long scond = DateUtil.between(DateUtil.date(), DateUtil.endOfDay(DateUtil.date()), DateUnit.SECOND);
            redisService.getValueOps().set(maxKey, "1", scond, TimeUnit.SECONDS);
            sentSmsVo.setLimit(codeMax - 1);
        } else {
            redisService.getValueOps().increment(maxKey);
            sentSmsVo.setLimit(codeMax - Integer.valueOf(smsMax));
        }
        DingTalkBo msg = new DingTalkBo();
        DingTalkBo.Text actionCard = msg.new Text();
        actionCard.setTitle("短信验证码");
        actionCard.setText(String.format("#### 发送手机短信验证码\n ---\n - 手机号：%s\n - 验证码：%s \n - 发送时间：%s", bo.getMobile(), code, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())));
        msg.setMsgtype("actionCard");
        msg.setActionCard(actionCard);
        try {
            DingTalkRestVo resp = dingTalkApi.send(accessToken, msg);
            log.info("dingding exception resp: {}", resp);
        } catch (Exception e){
            log.error("钉钉消息推送日常",e);
        }
        return sentSmsVo;
    }

    private void sentSms(TempSentSmsBo sentSmsBo, String mobile) {
        sentSmsBo.setToPhone(mobile);
        messageApi.sentSms(sentSmsBo);
    }

    private TempSentSmsBo getTemplateParam(String type, String code) {
        TempSentSmsBo sentSmsBo = new TempSentSmsBo();
        List<String> strings = new ArrayList<>();
        switch (SmsCaptchaNodeEnum.getByValue(type)) {
            case REGISTER:
                sentSmsBo.setTemplateCode(SmsTempCodeEnum.registered.getCode());
                strings.add(code);
                strings.add("5");
                break;
            case BIND_MOBILE:
                sentSmsBo.setTemplateCode(SmsTempCodeEnum.registered.getCode());
                strings.add(code);
                strings.add("5");
                break;
            case LOGIN:
                sentSmsBo.setTemplateCode(SmsTempCodeEnum.login.getCode());
                strings.add(code);
                strings.add("5");
                break;
            case RESET_PASSWORD:
                sentSmsBo.setTemplateCode(SmsTempCodeEnum.reset_password.getCode());
                strings.add(code);
                break;
        }
        sentSmsBo.setTemplateParam(strings);
        return sentSmsBo;
    }

    /**
     * 验证短信验证码
     */
    public String verifySMSCaptcha(String mobile, String vercode, SmsCaptchaNodeEnum node) {
        String key = getRedisKey(mobile, node);
        String code = redisService.getValueOps().get(key);
        if (code == null || !vercode.equals(code)) {
            throw new ParamException("验证码错误");
        }
        return key;
    }

    /**
     * 重置密码
     *
     * @param bo
     */
    @Transactional(rollbackFor = Throwable.class)
    public void resetPwd(ResetPwdBo bo) {
        User user = userDao.queryByMobile(bo.getMobile());
        ParamException.isNull(user, "请求参数异常");
        String key = RedisKey.USER_PASSWORD_RESET_TOKEN_KEY.getKey(user.getId());
        String code = redisService.getValueOps().get(key);
        if (code == null || !bo.getResetToken().equals(code)) {
            throw new ParamException("页面已过期请重新加载页面");
        }
        User update = new User();
        update.setId(user.getId());
        update.setPassword(EncryptUtil.encryptMD5(user.getSalt() + bo.getNewPwd()));
        userDao.updateByPrimaryKeySelective(update);
        template.delete(key);
    }

    /**
     * 查询用户的下级用户
     *
     * @param userId
     * @return
     */
    public List<UserBo> queryUserLower(String userId) {
        return BeanConverUtils.convertBeanList(userDao.queryUserLower(userId), UserBo.class);
    }

    /**
     * 查询OpenId是否注册过
     *
     * @return
     */
    public boolean openIdIsRegister(String openId) {
        return userAuthDao.getByOpenId(openId) != null;
    }

    public UserVo getProfileById(String userId, String pk) {
        UserVo vo = getById(userId);
        ParamException.isNull(vo, "用户信息不存在");
        vo.setUserAuthVo(getUserAuth(userId, pk));
        vo.setShareUserId(userId);
        return vo;
    }

    public InviteCodeVo getInviteCodeByRegion(RegionBo regionBo) {
        InviteCodeVo vo = new InviteCodeVo();
        RegionVo regionVo = gateApi.findRegionByBo(regionBo).getResult();
        if (regionVo != null) {
            String random = RandomUtil.randomString(8);
            vo.setInviteCode(random);
            redisService.getValueOps().set(RedisKey.INVITE_REGION_KEY.getKey(random), regionVo.getId(), 15, TimeUnit.MINUTES);
        } else {
            vo.setInviteCode("");
        }
        return vo;
    }

    @Transactional(rollbackFor = Exception.class)
    public void updateInviteCode(UpdateInviteCodeBo bo) {
        RestVo<OperateLogVo> restVo = dataApi.queryUpdateInviteCode(bo.getUserId());
        if (null != restVo) {
            OperateLogVo log = restVo.getResult();
            ParamException.isNotNull(log, "邀请人只能修改一次");
        }
        User sup = userDao.queryByKeyword(bo.getInviteCode());
        ParamException.isNull(sup, "邀请人不存在");
        ParamException.isTrue(sup.getId().equals(bo.getUserId()), "邀请人不能为自己");
        User user = userDao.selectByPrimaryKey(bo.getUserId());
        subtractPopularizeNum(user);
        User entity = new User();
        entity.setId(bo.getUserId());
        entity.setIndirectSuperior(sup.getImmediateSuperior());
        entity.setIndirectSuperiorName(sup.getImmediateSuperiorName());
        entity.setImmediateSuperior(sup.getId());
        entity.setImmediateSuperiorName(sup.getNickname());
        entity.setTopAgent(sup.getTopAgent());
        userDao.updateByPrimaryKeySelective(entity);
        addPopularizeNum(entity);
    }

    public void createFXUser(ProfitApplyVo vo) {
        User user = userDao.queryByMobile(vo.getPhone());
        if (null == user) {
            User entity = addUser(vo);
            addIdentityCard(vo, entity);
            syncUserStatus(vo, entity);
        }
    }

    private void syncUserStatus(ProfitApplyVo vo, User entity) {
        UserStatus userStatus = new UserStatus();
        userStatus.setUserId(entity.getId());
        UserStatus us = userStatusDao.selectOne(userStatus);

        Integer code = 0;
        if (BaseEnum.ApplyEnum.SUCCEED.getCode().equals(vo.getStatus())) {
            code = UserEnum.UserStatus.SUCCEED.getCode();
        }
        if (BaseEnum.ApplyEnum.CRE.getCode().equals(vo.getStatus())) {
            code = UserEnum.UserStatus.NUL.getCode();
        }
        if (BaseEnum.ApplyEnum.PRE.getCode().equals(vo.getStatus())) {
            code = UserEnum.UserStatus.PRE.getCode();
        }
        if (BaseEnum.ApplyEnum.FILL.getCode().equals(vo.getStatus())) {
            code = UserEnum.UserStatus.FILL.getCode();
        }
        //同步用户身份认证状态
        if (us != null) {
            us.setIdcardStatus(code);
            userStatusDao.updateIdCardById(us);
        } else {
            userStatus.setIdcardStatus(code);
            userStatus.setWxStatus(UserEnum.UserStatus.NUL.getCode());
            userStatus.setBankcardStatus(UserEnum.UserStatus.NUL.getCode());
            userStatusDao.insert(userStatus);
        }
    }

    private void addIdentityCard(ProfitApplyVo vo, User entity) {
        IdentityCard identityCard = new IdentityCard();
        identityCard.setUserId(entity.getId());
        identityCard.setIdCard(vo.getIdCard());
        identityCard.setAdder(vo.getAdder());
        identityCard.setIdCardFront(vo.getIdCardFront());
        identityCard.setIdCardBack(vo.getIdCardBack());
        identityCard.setIdName(vo.getIdName());
        identityCard.setPhone(vo.getPhone());
        identityCard.setRegionId(vo.getRegionId());
        identityCard.setStatus(vo.getStatus());
        identityCardDao.insert(identityCard);
    }

    private User addUser(ProfitApplyVo vo) {
        User entity = new User();
        entity.setId(UUIDGenerator.getShortUUID());
        entity.setSalt(RandomGenerateUtil.getRandomS(0, 6));
        entity.setMobile(vo.getPhone());
        entity.setType(UserEnum.UserTypeEnum.DIST.getValue());
        //获取邀请码自增序列
        Long index = redisService.getValueOps().increment(RedisKey.DATA_INVITATION_INCR.getKey(), 1);
        entity.setInviteCode(InvitationCodeUtil.idToCode(index));

        String wxaCode = getWxaCodeUnlimitUrl(entity.getId());
        entity.setWxaCodeUnlimit(wxaCode);
        entity.setProduct(BaseEnum.ProductEnum.FENXIAO.getName());
        entity.setRegionId(vo.getRegionId());
        entity.setNickname(CommonUtils.mobileEncrypt(entity.getMobile()));
        entity.setRealname(vo.getIdName());

        User sup = userDao.queryByRegion(vo.getRegionId());
        if (Objects.isNull(sup)) {
            sup = userDao.queryCompanyAccount();
        }
        if (Objects.nonNull(sup)) {
            entity.setImmediateSuperior(sup.getId());
            entity.setImmediateSuperiorName(sup.getNickname());
            entity.setTopAgent(sup.getId());
        }
        userDao.insert(entity);
        addPopularizeNum(entity);
        return entity;
    }

    public void createWxCode() {
        List<User> userList = userDao.selectAll();
        userList.stream().filter(user -> StrUtil.isEmpty(user.getWxaCodeUnlimit()))
                .forEach(user -> {
                    User entity = new User();
                    entity.setId(user.getId());
                    entity.setWxaCodeUnlimit(getWxaCodeUnlimitUrl(user.getId()));
                    userDao.updateByPrimaryKeySelective(entity);
                });
    }

    public UserVo getCompayAccount() {
        return BeanConverUtils.convertBean(userDao.queryCompanyAccount(), UserVo.class);
    }

    public String getUserIdByMobile(String phone) {
        User user = userDao.queryByMobile(phone);
        if (null != user) {
            return user.getId();
        }
        return null;
    }

    public void upToReionBoss(User entity) {
        userDao.upToReionBoss(entity);
        List<User> userList = userDao.queryDirectSubUser(entity.getId());
        userList.forEach(user -> {
            User directSub = new User();
            directSub.setId(user.getId());
            directSub.setTopAgent(entity.getId());
            userDao.updateSuperiorField(directSub);
        });
        recursionUpdateTopAgent(userList, entity.getId(), 0);
    }

    public void recursionUpdateTopAgent(List<User> userList, String topAgent, int i) {
        userList.forEach(user -> {
            List<User> userLists = userDao.queryDirectSubUser(user.getId());
            if (CollectionUtils.isEmpty(userLists)) {
                return;
            }
            if (i > 10) {
                return;
            }
            userDao.updateTopAgent(userLists.stream().map(BaseModel::getId).collect(Collectors.toList()), topAgent);
            recursionUpdateTopAgent(userLists, topAgent, i + 1);
        });
    }


    public String saveUserData(String userId, String content) {
        redisService.getValueOps().set(RedisKey.USER_DATA_KEY.getKey(userId), content, Constant.ACCESS_TOKEN_EXPIRE, TimeUnit.DAYS);
        return userId;
    }

    public String getUserData(String userId) {
        if (redisService.getValueOps().getOperations().hasKey(RedisKey.USER_DATA_KEY.getKey(userId))) {
            String content = redisService.getValueOps().get(RedisKey.USER_DATA_KEY.getKey(userId));
            redisService.getValueOps().getOperations().delete(RedisKey.USER_DATA_KEY.getKey(userId));
            return content;
        }
        return null;
    }

    public void provideSubsidy(UserSubsidyBo bo) {
        if (StrUtil.isEmpty(bo.getMsg())) {
            bo.setMsg("系统发放津贴");
        }
        ProvideSubsidyBo psb = BeanConverUtils.convertBean(bo, ProvideSubsidyBo.class);
        psb.setType(1);
        walletApi.provideSubsidy(psb);
    }

    public BigDecimal getInviteSubsidy() {
        return inviteSubsidy;
    }

    public Boolean isNewUser(String userId) {
        Instant startTime = com.ddwl.schema.util.DateUtil.addHours(Instant.now(), -24);
        Instant endTime = Instant.now();
        return userDao.isNewUser(userId, startTime, endTime);
    }

    public LoginVo checkRegister(RegisterBo bo) {
        if (BaseEnum.ProductEnum.BYTE_DANCE.getName().equals(bo.getProduct())) {
            return checkByteDanceRegister(bo);
        }
        UserAuth userAuth = userAuthDao.getByOpenId(bo.getWxOpenId());
        User user;
        if (userAuth != null) {
            user = userDao.selectByPrimaryKey(userAuth.getUserId());
            if (StringUtils.hasText(bo.getUnionId()) && !StringUtils.hasText(user.getUnionId())) {
                user.setUnionId(bo.getUnionId());
                userDao.updateByPrimaryKeySelective(user);
            }
            if (!StringUtils.hasText(user.getMobile()) && BaseEnum.ProductEnum.WBM.getName().equals(bo.getProduct())) {
                return null;
            }
            return getLoginVo(bo, user);
        }
        // 如何有UnionId 根据UnionId创建一个用户
        if (StringUtils.hasText(bo.getUnionId())) {
            user = userDao.getByUnionId(bo.getUnionId());
            if (user == null) {
                user = this.createUser(bo);
            }
        } else {
            user = this.createUser(bo);
        }
        if (null == userAuth) {
            addUserAuth(bo, user, bo.getWxOpenId());
        }
        if (!StringUtils.hasText(user.getMobile()) && BaseEnum.ProductEnum.WBM.getName().equals(bo.getProduct())) {
            return null;
        }
        return getLoginVo(bo, user);
    }

    private LoginVo checkByteDanceRegister(RegisterBo bo) {
        UserAuth userAuth = userAuthDao.getByOpenId(bo.getByteOpenId());
        User user;
        if (userAuth != null) {
            user = userDao.selectByPrimaryKey(userAuth.getUserId());
            if (null != user) {
                if (StringUtils.hasText(bo.getByteUnionId()) && !StringUtils.hasText(user.getByteUnionId())) {
                    user.setByteUnionId(bo.getByteUnionId());
                    userDao.updateByPrimaryKeySelective(user);
                }
                return getLoginVo(bo, user);
            }
        }
        // 如何有UnionId 根据UnionId创建一个用户
        if (StringUtils.hasText(bo.getByteUnionId())) {
            user = userDao.getByByteUnionId(bo.getByteUnionId());
            if (null != user) {
                userAuth = userAuthDao.getByUserId(user.getId(), bo.getProduct());
                if (null != userAuth) {
                    if (StringUtils.hasText(bo.getByteOpenId())) {
                        userAuth.setOpenId(bo.getByteOpenId());
                        userAuthDao.updateOtherAuth(userAuth, bo.getProduct());
                    }
                } else {
                    addUserAuth(bo, user, bo.getByteOpenId());
                }
                return getLoginVo(bo, user);
            }
        }
        return null;
    }

    private void addUserAuth(RegisterBo bo, User user, String wxOpenId) {
        UserAuth userAuth;
        userAuth = new UserAuth();
        userAuth.setUserId(user.getId());
        userAuth.setOpenId(wxOpenId);
        userAuth.setProduct(bo.getProduct());
        userAuth.setAppId(bo.getAppId());
        userAuthDao.add(userAuth);
    }

    private LoginVo getLoginVo(RegisterBo bo, User user) {
        LoginVo vo = BeanConverUtils.convertBean(user, LoginVo.class);
        LoginBo loginBo = new LoginBo();
        ShopVo shopVo = shopApi.getSimpleShopByUserId(user.getId()).getResult();
        if (null != shopVo) {
            loginBo.setShopId(shopVo.getId());
        }
        loginBo.setId(user.getId());
        loginBo.setNickname(user.getNickname());
        loginBo.setUsername(user.getMobile());
        loginBo.setProduct(bo.getProduct());
        loginBo.setDk(bo.getDk());
        vo.setUserName(user.getMobile());
        vo.setToken(tokenService.getToken(loginBo));
        return vo;
    }

    public void saveShopWxaCode(UserBo bo) {
        User user = BeanConverUtils.convertBean(bo, User.class);
        userDao.updateByPrimaryKeySelective(user);
    }

    public void updateWxUserInfo(WxUserBo bo) {
        User wbm = userDao.selectByPrimaryKey(bo.getUserId());
        if (wbm == null) {
            throw new CommonException("用户不存在");
        }
        User user = BeanConverUtils.convertBean(bo, User.class);
        user.setId(bo.getUserId());
        user.setNickname(bo.getNickName());
        user.setAvatar(Optional.ofNullable(user.getAvatar()).orElse("https://58d.oss-cn-hangzhou.aliyuncs.com/avatar/2021-06-24/1624516998985.png"));
        userDao.updateByPrimaryKeySelective(user);
        UserAuth userAuth = BeanConverUtils.convertBean(bo, UserAuth.class);
        userAuthDao.updateByUserIdAndPk(userAuth, bo.getProduct());
        updateOtherAuth(bo, wbm.getId());
    }

    private void updateOtherAuth(WxUserBo bo, String userId) {
        UserAuth userAuth = BeanConverUtils.convertBean(bo, UserAuth.class, "appId");
        userAuth.setUserId(userId);
        userAuthDao.updateOtherAuth(userAuth, bo.getProduct());
    }

    public UserVo getByOpenId(String openId, String pk) {
        return userDao.getByOpenId(openId, pk);
    }

    @Transactional(rollbackFor = Exception.class)
    public void userAuthJobHandler() {
//        List<UserAuthDto> dtos = userAuthDao.getWbmAuth();
//        List<String> mobiles = dtos.stream().map(UserAuthDto::getMobile).collect(Collectors.toList());
//        if (!CollectionUtils.isEmpty(mobiles)) {
//            List<UserAuth> inserts = new ArrayList<>();
//            List<User> users = userDao.getFenXiaoNotAuth(mobiles);
//            users.forEach(uu -> {
//                UserAuthDto userAuthDto = dtos.stream().filter(dto -> uu.getMobile().equals(dto.getMobile())).findFirst().orElse(null);
//                if (null != userAuthDto) {
//                    UserAuth ua = BeanConverUtils.convertBean(userAuthDto, UserAuth.class);
//                    ua.setUserId(uu.getId());
//                    ua.setAppId(WxEnum.FENXIAO_APP_APPID);
//                    ua.setProduct(BaseEnum.ProductEnum.FENXIAO.getName());
//                    inserts.add(ua);
//                }
//            });
//            if (!CollectionUtils.isEmpty(inserts)) {
//                userAuthDao.insertList(inserts);
//            }
//        }
    }

    /**
     * 获取五八马公众号openId
     *
     * @param userId
     * @return
     */
    public String getWechatOpenId(String userId) {
        UserAuth ua = userAuthDao.getByUserId(userId, BaseEnum.ProductEnum.WBM_OFFICE.getName());
        if (null != ua) {
            return ua.getOpenId();
        }
        return null;
    }

    public UserVo getCharityFundUser() {
        //TODO 获取公益基金用户
        return null;
    }

    /**
     * 修改用户上下级关系与代理等级
     *
     * @param bo
     */
    @Transactional(rollbackFor = Throwable.class)
    public void changeAgent(UserAgentBo bo) {
        User user = userDao.selectByPrimaryKey(bo.getUserId());
        ParamException.isNull(user);
        // 提升用户代理等级
        if (bo.getProfitLevel() != null) {
            user.setProfitLevel(bo.getProfitLevel());
        }
        if (StringUtils.hasText(bo.getImmediateSuperior()) && !bo.getImmediateSuperior().equals(user.getImmediateSuperior())) {
            User immediateSuperiorUser = userDao.selectByPrimaryKey(bo.getImmediateSuperior());
            ParamException.isNull(immediateSuperiorUser);
            user.setImmediateSuperior(immediateSuperiorUser.getId());
            user.setImmediateSuperiorName(Optional.ofNullable(immediateSuperiorUser.getRealname()).orElse(immediateSuperiorUser.getNickname()));
            user.setIndirectSuperior(immediateSuperiorUser.getImmediateSuperior());
            user.setIndirectSuperiorName(immediateSuperiorUser.getImmediateSuperiorName());
        }
        userDao.updateByPrimaryKeySelective(user);
        // 如果是个代 发放4张兑换券
        if (!UserEnum.ProfitLevelEnum.PERSONAL.getLevel().equals(user.getProfitLevel()) && UserEnum.ProfitLevelEnum.PERSONAL.getLevel().equals(bo.getProfitLevel())) {
            IssueCouponBo issueCouponBo = new IssueCouponBo();
            issueCouponBo.setUserId(bo.getUserId());
            issueCouponBo.setCouponId(gateApi.getSettingVal(GateEnum.DictionaryTag.TAG_SYSTEM.name(), GateEnum.DictionaryField.AGENT_COUPON_ID.name()).getResult());
            issueCouponBo.setQty(4);
            walletApi.issueCoupon(Collections.singletonList(issueCouponBo));
        }
    }

    public List<User> selectSubAgentUser(UserAgentQueryBo bo) {
        return userDao.selectSubAgentUser(bo);
    }


    public UserVo getUserByKey(String keyWord) {
        return BeanConverUtils.convertBean(userDao.queryByKeyword(keyWord), UserVo.class);
    }
}
